// Copyright (C) 2006 Taylor L. Riché <riche@cs.utexas.edu>
//  
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// 	$Id: PhysInterface.cc 418 2006-05-26 16:33:07Z richetl $	
//
// now using subversion
#include "PhysInterface.hh"

using namespace lee;

PhysInterface::PhysInterface(std::string intf) : NetInterface(intf), fromSet(false) 
{
  Synchronized::Scope mutex(this);
  coreSocket = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
  struct ifreq myReq;
  if(coreSocket == -1) {
    std::cerr << "Problem with opening socket on device " << intf << std::endl;
    exit(1);
  }
  else {
    memset((void *)&myReq, '\0', sizeof(struct ifreq));
    memcpy(myReq.ifr_name, intf.c_str(), sizeof(intf.c_str()));
    if(ioctl(coreSocket, SIOCGIFINDEX, &myReq) < 0) {
      std::cerr << "Problem with ioctl: " << strerror(errno) << std::endl;
      exit(1);
    }
    intfNum = myReq.ifr_ifindex;
    struct sockaddr_ll myOpts;
    myOpts.sll_family = AF_PACKET;
    myOpts.sll_protocol = htons(ETH_P_ALL);
    myOpts.sll_ifindex = intfNum;
    unsigned int bindReturn = bind(coreSocket, (struct sockaddr *)&myOpts,
				   sizeof(struct sockaddr_ll));
    if(bindReturn != 0) {
      close(coreSocket);
      std::cerr << "Problem with binding: " << strerror(bindReturn) << std::endl;
      exit(1);
    }
  }
  socklen_t namelen = sizeof(coreFrom);
  memset(&coreFrom, '\0', namelen);
  coreFrom.sll_family = AF_PACKET;
  coreFrom.sll_ifindex = intfNum;
  if(ioctl(coreSocket, SIOCGIFHWADDR, &myReq) < 0) {
    std::cerr << "Problem with ioctl: " << strerror(errno) << std::endl;
    exit(1);
  }
  //  memcpy(coreFrom.sll_addr, myReq.ifr_hwaddr.sa_data, 6);
  // Set broadcast address for destination MAC address
  for(int i = 0; i < 6; ++i) {
    coreFrom.sll_addr[i] = 0xff;
  }
  coreFrom.sll_halen = 6;
  coreFrom.sll_hatype = coreFrom.sll_pkttype = coreFrom.sll_protocol = 0;
  fromSet = true;
  MethodWrapper<PhysInterface> * mw = new MethodWrapper<PhysInterface>;
  mw->object = this;
  mw->method = &PhysInterface::process;
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  if(pthread_create(&coreThread, &attr, plainFunctionNI, (void *)mw)) {
    std::cerr << "pthread_create failed" << std::endl;
    std::exit(1);
  }

}

void
PhysInterface::process()
{
  waitForSetup();
  assert(!isRecursive());
  struct sockaddr_ll from;
  while(true) {
    unsigned char * newpkt = new unsigned char[BUFSIZE];
    ssize_t pktSize;
    socklen_t namelen = sizeof(from);
    pktSize = recvfrom(coreSocket, newpkt, BUFSIZE, 0, (struct sockaddr *)&from, &namelen);
    if(pktSize < 0) {
      std::cerr << "Problem with receiving packet on device " << intfName << std::endl;
      std::cerr << errno << ": " << strerror(errno) << std::endl;
      exit(1);
    }
    if(from.sll_pkttype != PACKET_OUTGOING) {
      Composable * nextComp = ag->getNextComposable(intfName, IN);
      if(nextComp == NULL) {
	delete[] newpkt;
      }
      else {
	Function * nextFunc;
	nextFunc = dynamic_cast<Function *>(nextComp);
	PCon * newPCon = new PCon();
	ProcUnit * nextProc;
	{
	  Synchronized::Scope mutex(newPCon);
	  if(nextFunc != NULL) {
	    // the next composable is just a function
	    newPCon->setFunction(nextFunc);
	  }
	  else {
	    // the next composable is a module
	    while(nextFunc == NULL) {
	      Module * mod = dynamic_cast<Module *>(nextComp);
	      assert(mod != NULL);
	      // this is an entry into a module
	      nextComp = mod->getStart();
	      nextFunc = dynamic_cast<Function *>(nextComp);
	    }
	    newPCon->setFunction(nextFunc);
	  }
	  assert(newpkt != NULL);
	  newPCon->setPacket(static_cast<PktData *>(newpkt));
	  newPCon->setSize(pktSize);
	  nextProc = newPCon->getFunction()->getProc();
	  assert(newPCon->getPacket() != NULL);
	}
	nextProc->executePCon(newPCon);
      }
    }
  }
  return;
}

void
PhysInterface::processData(PktData * const pkt, uint32_t pktSize)
{
  assert(pkt != NULL);
  while(!fromSet) {
    wait();
  }
  int r = sendto(coreSocket, (char *)pkt, pktSize, 0, (sockaddr *)&coreFrom, sizeof(coreFrom));
  if(r < 0) {
    std::cerr << "Problem with sending packet on device " << intfName << std::endl;
    std::cerr << errno << ": " << strerror(errno) << std::endl;
    exit(1);
  }
  char * delPkt = (char *)pkt;
  delete[] delPkt;
  return;
}
